package com.nulldev.util.osAPI;

import java.io.File;
import java.io.IOException;
import java.util.Locale;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.prefs.Preferences;

import com.nulldev.util.JVM.JVM;
import com.nulldev.util.VariableAPI.MathUtil;
import com.nulldev.util.manage.experiments.Experiments;

public class OS {
	public enum OperatingSystem {
		Windows("Windows"),
		MacOS("Mac OS"),
		Android("Android (Linux)"),
		Linux("GNU/Linux"),
		Solaris("Solaris"),
		BSD("BSD"),
		Other("N/A");

		private final String displayOS;

		private OperatingSystem(final String displayOS) {
			this.displayOS = displayOS;
		}

		@Override
		public String toString() {
			return this.displayOS;
		}
	};

	private static OperatingSystem detectedOS;

	public static OperatingSystem get() {
		if (detectedOS == null) {
			final String OS = System.getProperty("os.name", "generic").toLowerCase(Locale.ENGLISH);
			if ((OS.indexOf("mac") >= 0) || (OS.indexOf("darwin") >= 0)) {
				return detectedOS = OperatingSystem.MacOS;
			} else if (OS.indexOf("win") >= 0) {
				return detectedOS = OperatingSystem.Windows;
			} else if (OS.indexOf("nux") >= 0) {
				return detectedOS = OperatingSystem.Linux;
			} else if (OS.indexOf("solaris") >= 0 || OS.indexOf("sunos") >= 0) {
				return detectedOS = OperatingSystem.Solaris;
			} else if (OS.indexOf("BSD") >= 0) {
				return detectedOS = OperatingSystem.BSD;
			} else {
				return detectedOS = OperatingSystem.Other;
			}
		} else
			return detectedOS;
	}

	public static boolean isLinux() {
		if (detectedOS == null) {
			detectedOS = get();
		}
		return detectedOS.equals(OperatingSystem.Linux);
	}

	public static boolean isSolaris() {
		if (detectedOS == null) {
			detectedOS = get();
		}
		return detectedOS.equals(OperatingSystem.Solaris);
	}

	public static enum Windows_Type {
		WINDOWS_11(11.0, "Windows 11"),
		WINDOWS_10(10.0, "Windows 10"),
		WINDOWS_8_1(6.3, "Windows 8.1"),
		WINDOWS_8(6.2, "Windows 8"),
		WINDOWS_7(6.1, "Windows 7"),
		WINDOWS_VISTA(6, "Windows Vista"),
		WINDOWS_XP_P64(5.2, "Windows XP [Professional x64]"), /*
																 * Minimum supported version for nullUtil 3 . x
																 */
		WINDOWS_XP(5.1, "Windows XP"), /* Minimum supported version for nullUtil 2.x */
		WINDOWS_2000(5.0, "Windows 2000"),
		WINDOWS_ME(4.9, "Windows ME"),
		WINDOWS_98(4.10, "Windows 98"),
		WINDOWS_95(4, "Windows 95"),
		WINDOWS_NT_3_51(3.51, "Windows NT 3.51"),
		WINDOWS_NT_3_5(3.5, "Windows NT 3.5"),
		WINDOWS_3_2(3.2, "Windows 3.2"),

		N_A(-1, "N/A");

		private final double ntVer;
		private final String ntString;

		private Windows_Type(final double ntVer, final String ntString) {
			this.ntVer = ntVer;
			this.ntString = ntString;
		}

		@Override
		public String toString() {
			return this.ntString;
		}

		public String getNTString() {
			return this.ntString;
		}

		public double getNT() {
			return this.ntVer;
		}

		public static Windows_Type getType() {
			if (!OS.isWindows()) {
				return N_A;
			}
			final double ntVerI = MathUtil.safeParseDouble(System.getProperty("os.version", "generic"), -1);
			for (final Windows_Type type : values()) {
				if (type.getNT() == ntVerI) {
					return type;
				}
			}
			return N_A;
		}
	}

	public static boolean isWindows() {
		return get() == OperatingSystem.Windows;
	}

	public static boolean isBSD() {
		return get() == OperatingSystem.BSD;
	}

	public static boolean isMacOS() {
		return get() == OperatingSystem.MacOS;
	}

	public static boolean isAndroid() {
		return get() == OperatingSystem.Android;
	}

	private static final boolean IS_PRIV_USE_NEW_WINDOWS_LOGIC = Experiments.get("os://enable-new-detect-logic/").isActive();
	private static AtomicBoolean IS_PRIV_CACHED_WINDOWS_LOGIC;

	public static boolean isPriv() {
		if (isWindows()) {
			if (IS_PRIV_USE_NEW_WINDOWS_LOGIC) {
				if (IS_PRIV_CACHED_WINDOWS_LOGIC != null) {
					return IS_PRIV_CACHED_WINDOWS_LOGIC.get();
				} else {
					final Preferences preferences = Preferences.systemRoot();
					try {
						preferences.put("nullutil3_test", "foobar"); // Generates a SecurityException on Windows
						preferences.remove("nullutil3_test");
						preferences.flush(); // Generates a BackingStoreException on Linux
						return (IS_PRIV_CACHED_WINDOWS_LOGIC = new AtomicBoolean(true)).get();
					} catch (Exception ex) {
						return (IS_PRIV_CACHED_WINDOWS_LOGIC = new AtomicBoolean(false)).get();
					} catch (Throwable t) {
						JVM.errorln(t);
						return false;
					}
				}
			} else {
				try {
					final File f = new File("C:\\Program Files\\.nu3.test");
					f.createNewFile();
					f.deleteOnExit();
					return true;
				} catch (IOException e) {
					return false;
				}
			}
		} else if (isLinux() || isBSD() || isMacOS()) {
			return System.getenv().getOrDefault("USER", "").equalsIgnoreCase("root");
		} else {
			return false;
		}
	}
}
